package vivace.model;

import javax.sound.midi.*;

import vivace.exception.NoChannelAssignedException;

/**
 * Class that represents a note, consisting of a NOTE_ON and a NOTE_OFF event.
 */
public class NoteEvent implements Comparable<NoteEvent> {

	private int trackIndex;
	private MidiEvent noteOn,noteOff;

	/**
	 * Returns to which track the note belongs to
	 * @return
	 */
	public int getTrackIndex() {
		return trackIndex;
	}
	
	/**
	 * Returns the NOTE_ON event for the note
	 * @return
	 */
	public MidiEvent getNoteOn() {
		return noteOn;
	}
	
	/**
	 * Returns the NOTE_OFF event for the note
	 * @return
	 */
	public MidiEvent getNoteOff() {
		return noteOff;
	}
	
	/**
	 * Returns the note value of the note
	 * @return
	 */
	public int getNoteValue(){
		return ((ShortMessage) getNoteOn().getMessage()).getData1();
	}
	
	/**
	 * Returns the velocity of the note
	 * @return
	 */
	public int getVelocity(){
		return ((ShortMessage) getNoteOn().getMessage()).getData2();
	}
	
	/**
	 * Constructor that creates a new note (one NOTE_ON and one NOTE_OFF event) 
	 * with the specified value between the specified ticks
	 * @param trackIndex The track to which the note belongs
	 * @param noteValue The note value of the note
	 * @param fromTick The tick when the note should start
	 * @param toTick The tick when the note should end
	 */
	public NoteEvent( int trackIndex, int noteValue, long fromTick, long toTick ){

		// Create two messages for the note on and the note off event
		ShortMessage smOn = new ShortMessage();
		ShortMessage smOff = new ShortMessage();

		// -1 for easy to check if the getTrackChannel was succesfull
		Integer trackChannel = -1;
		try{
			//Get the channel for which the track use
			trackChannel = App.Project.getTrackChannel(trackIndex);
		} catch (NoChannelAssignedException e){
			e.printStackTrace(); // TODO: Hantera!
		}

		//Check if the getTrackChannel was succesfull
		try{
			// Set the message to a NOTE_ON on CHANNEL for the track on a specific note with velocity 100 (default)
			smOn.setMessage(ShortMessage.NOTE_ON, trackChannel, noteValue, 100);
		} catch (InvalidMidiDataException e) {
			e.printStackTrace(); // TODO: Hantera!
		}

		try{
			// Set the message to a NOTE_OFF on CHANNEL for the track on a specific note with velocity 100 (default)
			smOff.setMessage(ShortMessage.NOTE_OFF, trackChannel, noteValue, 100);
		} catch (InvalidMidiDataException e) { }

		// Set the fields
		this.trackIndex = trackIndex;
		this.noteOn = new MidiEvent(smOn, fromTick);
		this.noteOff = new MidiEvent(smOff, toTick);

	}

	/**
	 * Constructor, creates a new note from the NOTE_ON and the NOTE_OFF events
	 * @param trackIndex The track to which the note belongs
	 * @param noteOn The NOTE_ON event for the note
	 * @param noteOff The NOTE_OFF event for the note
	 */
	public NoteEvent( int trackIndex, MidiEvent noteOn, MidiEvent noteOff ){
		this.trackIndex = trackIndex;
		this.noteOn = noteOn;
		this.noteOff = noteOff;
	}
	

	@Override
	public int hashCode() {
		// The hash code only depends on the note on event, since we assumes that 
		// two notes are equal if they have the same note on event.
		final int prime = 31;
		int result = 1;
		result = prime * result + ((noteOn == null) ? 0 : noteOn.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		// The equals method only depends on the note on event, since we assumes that 
		// two notes are equal if they have the same note on event.
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final NoteEvent other = (NoteEvent) obj;
		if (noteOn == null) {
			if (other.noteOn != null)
				return false;
		} else if (!noteOn.equals(other.noteOn))
			return false;
		return true;
	}

	@Override
	public int compareTo(NoteEvent ne) {
		// The default implementation is to compare the note on event's tick
		return (int) (this.getNoteOn().getTick() - ne.getNoteOn().getTick());
	}

		
}
